home *** CD-ROM | disk | FTP | other *** search
/ CD ROM Paradise Collection 4 / CD ROM Paradise Collection 4 1995 Nov.iso / hobby / ast44src.zip / GENERAL.C < prev    next >
C/C++ Source or Header  |  1995-02-11  |  26KB  |  997 lines

  1. /*
  2. ** Astrolog (Version 4.40) File: general.c
  3. **
  4. ** IMPORTANT NOTICE: The graphics database and chart display routines
  5. ** used in this program are Copyright (C) 1991-1995 by Walter D. Pullen
  6. ** (astara@u.washington.edu). Permission is granted to freely use and
  7. ** distribute these routines provided one doesn't sell, restrict, or
  8. ** profit from them in any way. Modification is allowed provided these
  9. ** notices remain with any altered or edited versions of the program.
  10. **
  11. ** The main planetary calculation routines used in this program have
  12. ** been Copyrighted and the core of this program is basically a
  13. ** conversion to C of the routines created by James Neely as listed in
  14. ** Michael Erlewine's 'Manual of Computer Programming for Astrologers',
  15. ** available from Matrix Software. The copyright gives us permission to
  16. ** use the routines for personal use but not to sell them or profit from
  17. ** them in any way.
  18. **
  19. ** The PostScript code within the core graphics routines are programmed
  20. ** and Copyright (C) 1992-1993 by Brian D. Willoughby
  21. ** (brianw@sounds.wa.com). Conditions are identical to those above.
  22. **
  23. ** The extended accurate ephemeris databases and formulas are from the
  24. ** calculation routines in the program "Placalc" and are programmed and
  25. ** Copyright (C) 1989,1991,1993 by Astrodienst AG and Alois Treindl
  26. ** (alois@azur.ch). The use of that source code is subject to
  27. ** regulations made by Astrodienst Zurich, and the code is not in the
  28. ** public domain. This copyright notice must not be changed or removed
  29. ** by any user of this program.
  30. **
  31. ** Initial programming 8/28,30, 9/10,13,16,20,23, 10/3,6,7, 11/7,10,21/1991.
  32. ** X Window graphics initially programmed 10/23-29/1991.
  33. ** PostScript graphics initially programmed 11/29-30/1992.
  34. ** Last code change made 1/29/1995.
  35. */
  36.  
  37. #include "astrolog.h"
  38.  
  39.  
  40. /*
  41. ******************************************************************************
  42. ** General Procedures.
  43. ******************************************************************************
  44. */
  45.  
  46. /* Swap two floating point values. */
  47.  
  48. void SwapR(d1, d2)
  49. real *d1, *d2;
  50. {
  51.   real temp;
  52.  
  53.   temp = *d1; *d1 = *d2; *d2 = temp;
  54. }
  55.  
  56.  
  57. /* Return the length of a string (not counting the null terminator). */
  58.  
  59. int CchSz(sz)
  60. CONST char *sz;
  61. {
  62.   int i;
  63.  
  64.   for (i = 0; *sz++; i++)
  65.     ;
  66.   return i;
  67. }
  68.  
  69.  
  70. /* Compare two strings. Return 0 if they are equal, a positive value if  */
  71. /* the first string is greater, and a negative if the second is greater. */
  72.  
  73. int NCompareSz(s1, s2)
  74. CONST char *s1, *s2;
  75. {
  76.   while (*s1 && *s1 == *s2)
  77.     s1++, s2++;
  78.   return *s1 - *s2;
  79. }
  80.  
  81.  
  82. /* Set a given number of bytes to zero given a starting pointer. */
  83.  
  84. void ClearB(pb, cb)
  85. lpbyte pb;
  86. int cb;
  87. {
  88.   while (cb-- > 0)
  89.     *pb++ = 0;
  90. }
  91.  
  92.  
  93. /* Copy a given number of bytes from one location to another. */
  94.  
  95. void CopyRgb(pbSrc, pbDst, cb)
  96. byte *pbSrc, *pbDst;
  97. int cb;
  98. {
  99.   while (cb-- > 0)
  100.     *pbDst++ = *pbSrc++;
  101. }
  102.  
  103.  
  104. /* Determine the sign of a number: -1 if value negative, +1 if value */
  105. /* positive, and 0 if it's zero.                                     */
  106.  
  107. real RSgn(r)
  108. real r;
  109. {
  110.   return r == 0.0 ? 0.0 : RSgn2(r);
  111. }
  112.  
  113.  
  114. /* Given an x and y coordinate, return the angle formed by a line from the */
  115. /* origin to this coordinate. This is just converting from rectangular to  */
  116. /* polar coordinates; however, we don't determine the radius here.         */
  117.  
  118. real Angle(x, y)
  119. real x, y;
  120. {
  121.   real a;
  122.  
  123.   if (x != 0.0) {
  124.     if (y != 0.0)
  125.       a = RAtn(y/x);
  126.     else
  127.       a = x < 0.0 ? rPi : 0.0;
  128.   } else
  129.     a = y < 0.0 ? -rPiHalf : rPiHalf;
  130.   if (a < 0.0)
  131.     a += rPi;
  132.   if (y < 0.0)
  133.     a += rPi;
  134.   return a;
  135. }
  136.  
  137.  
  138. /* Modulus function for floating point values, where we bring the given */
  139. /* parameter to within the range of 0 to 360.                           */
  140.  
  141. real Mod(d)
  142. real d;
  143. {
  144.   if (d >= rDegMax)        /* In most cases, our value is only slightly */
  145.     d -= rDegMax;          /* out of range, so we can test for it and   */
  146.   else if (d < 0.0)        /* avoid the more complicated arithmetic.    */
  147.     d += rDegMax;
  148.   if (d >= 0 && d < rDegMax)
  149.     return d;
  150.   return (d - RFloor(d/rDegMax)*rDegMax);
  151. }
  152.  
  153.  
  154. /* Another modulus function, this time for the range of 0 to 2 Pi. */
  155.  
  156. real ModRad(r)
  157. real r;
  158. {
  159.   while (r >= rPi2)    /* We assume our value is only slightly out of       */
  160.     r -= rPi2;         /* range, so test and never do any complicated math. */
  161.   while (r < 0.0)
  162.     r += rPi2;
  163.   return r;
  164. }
  165.  
  166.  
  167. /* Integer division - like the "/" operator but always rounds result down. */
  168.  
  169. long Dvd(x, y)
  170. long x, y;
  171. {
  172.   long z;
  173.  
  174.   if (y == 0)
  175.     return x;
  176.   z = x / y;
  177.   if (((x >= 0) == (y >= 0)) || x-z*y == 0)
  178.     return z;
  179.   return z - 1;
  180. }
  181.  
  182.  
  183. /*
  184. ******************************************************************************
  185. ** General Astrology Procedures.
  186. ******************************************************************************
  187. */
  188.  
  189. /* A similar modulus function: convert an integer to value from 1..12. */
  190.  
  191. int Mod12(i)
  192. int i;
  193. {
  194.   while (i > cSign)
  195.     i -= cSign;
  196.   while (i < 1)
  197.     i += cSign;
  198.   return i;
  199. }
  200.  
  201.  
  202. /* Convert an inputed fractional degrees/minutes value to a true decimal   */
  203. /* degree quantity. For example, the user enters the decimal value "10.30" */
  204. /* to mean 10 degrees and 30 minutes; this will return 10.5, i.e. 10       */
  205. /* degrees and 30 minutes expressed as a floating point degree value.      */
  206.  
  207. real DecToDeg(d)
  208. real d;
  209. {
  210.   return RSgn(d)*(RFloor(RAbs(d))+RFract(RAbs(d))*100.0/60.0);
  211. }
  212.  
  213.  
  214. /* This is the inverse of the above function. Given a true decimal value */
  215. /* for a zodiac degree, adjust it so the degrees are in the integer part */
  216. /* and the minute expressed as hundredths, e.g. 10.5 degrees -> 10.30    */
  217.  
  218. real DegToDec(d)
  219. real d;
  220. {
  221.   return RSgn(d)*(RFloor(RAbs(d))+RFract(RAbs(d))*60.0/100.0);
  222. }
  223.  
  224.  
  225. /* Return the shortest distance between two degrees in the zodiac. This is  */
  226. /* normally their difference, but we have to check if near the Aries point. */
  227.  
  228. real MinDistance(deg1, deg2)
  229. real deg1, deg2;
  230. {
  231.   real i;
  232.  
  233.   i = RAbs(deg1-deg2);
  234.   return i < rDegHalf ? i : rDegMax - i;
  235. }
  236.  
  237.  
  238. /* This is just like the above routine, except the min distance value  */
  239. /* returned will either be positive or negative based on whether the   */
  240. /* second value is ahead or behind the first one in a circular zodiac. */
  241.  
  242. real MinDifference(deg1, deg2)
  243. real deg1, deg2;
  244. {
  245.   real i;
  246.  
  247.   i = deg2 - deg1;
  248.   if (RAbs(i) < rDegHalf)
  249.     return i;
  250.   return RSgn(i)*(RAbs(i) - rDegMax);
  251. }
  252.  
  253.  
  254. /* Return the degree of the midpoint between two zodiac positions, making */
  255. /* sure we return the true midpoint closest to the positions in question. */
  256.  
  257. real Midpoint(deg1, deg2)
  258. real deg1, deg2;
  259. {
  260.   real mid;
  261.  
  262.   mid = (deg1+deg2)/2.0;
  263.   return MinDistance(deg1, mid) < rDegQuad ? mid : Mod(mid+rDegHalf);
  264. }
  265.  
  266.  
  267. /* Given a planet and sign, determine whether: The planet rules the sign, */
  268. /* the planet has its fall in the sign, the planet exalts in the sign, or */
  269. /* is debilitated in the sign; and return an appropriate character.       */
  270.  
  271. char Dignify(obj, sign)
  272. int obj, sign;
  273. {
  274.   if (obj > oNorm)
  275.     return ' ';
  276.   if (ruler1[obj] == sign || ruler2[obj] == sign)
  277.     return 'R';
  278.   if (ruler1[obj] == Mod12(sign+6) || ruler2[obj] == Mod12(sign+6))
  279.     return 'F';
  280.   if (exalt[obj] == sign)
  281.     return 'e';
  282.   if (exalt[obj] == Mod12(sign+6))
  283.     return 'd';
  284.   return '-';
  285. }
  286.  
  287.  
  288. /* Determine the number of days in a particular month. The year is needed, */
  289. /* too, because we have to check for leap years in the case of February.   */
  290.  
  291. int DayInMonth(month, year)
  292. int month, year;
  293. {
  294.   int d;
  295.  
  296.   if (month == mSep || month == mApr || month == mJun || month == mNov)
  297.     d = 30;
  298.   else if (month != mFeb)
  299.     d = 31;
  300.   else {
  301.     d = 28;
  302.     if (year % 4 == 0 &&
  303.       (year % 100 != 0 || year % 400 == 0 || year <= yeaJ2G))
  304.       d++;
  305.   }
  306.   return d;
  307. }
  308.  
  309.  
  310. /* Return the actual number of days in a particular month. Normally, this  */
  311. /* is the same as the above routine which determines the index of the last */
  312. /* day of the month, but the values can differ when changing between       */
  313. /* calendar systems (Julian to Gregorian) in which one can jump over days. */
  314.  
  315. int DaysInMonth(month, year)
  316. int month, year;
  317. {
  318.   int d;
  319.  
  320.   d = DayInMonth(month, year);
  321.   if (year == yeaJ2G && month == monJ2G)
  322.     d -= (dayJ2G2 - dayJ2G1 - 1);
  323.   return d;
  324. }
  325.  
  326.  
  327. /* Return the day of the week (Sunday is 0) of the specified given date. */
  328.  
  329. int DayOfWeek(month, day, year)
  330. int month, day, year;
  331. {
  332.   int d;
  333.  
  334.   d = (int)((MdyToJulian(month, day, year) + 1) % 7);
  335.   return d < 0 ? d+7 : d;
  336. }
  337.  
  338.  
  339. /* Given a day, and the month and year it falls in, add a number of days    */
  340. /* to it and return the new day index. As month changes are not checked for */
  341. /* here, this is mostly just adding the offset to the day; however we need  */
  342. /* to check for calendar changes for when days in a month may be skipped.   */
  343.  
  344. int AddDay(month, day, year, delta)
  345. int month, day, year, delta;
  346. {
  347.   int d;
  348.  
  349.   d = day + delta;
  350.   if (year == yeaJ2G && month == monJ2G) {     /* Check for Julian to  */
  351.     if (d > dayJ2G1 && d < dayJ2G2)            /* Gregorian crossover. */
  352.       d += NSgn(delta)*(dayJ2G2-dayJ2G1-1);
  353.   }
  354.   return d;
  355. }
  356.  
  357.  
  358. /* Given an aspect and two objects making that aspect with each other,   */
  359. /* return the maximum orb allowed for such an aspect. Normally this only */
  360. /* depends on the aspect itself, but some objects require narrow orbs,   */
  361. /* and some allow wider orbs, so check for these cases.                  */
  362.  
  363. real GetOrb(obj1, obj2, asp)
  364. int obj1, obj2, asp;
  365. {
  366.   real orb, i;
  367.  
  368.   orb = aspectorb[asp];
  369.   i = obj1 > oNorm ? 2.0 : planetorb[obj1];
  370.   orb = Min(orb, i);
  371.   i = obj2 > oNorm ? 2.0 : planetorb[obj2];
  372.   orb = Min(orb, i);
  373.   if (obj1 <= oNorm)
  374.     orb += planetadd[obj1];
  375.   if (obj2 <= oNorm)
  376.     orb += planetadd[obj2];
  377.   return orb;
  378. }
  379.  
  380.  
  381. /*
  382. ******************************************************************************
  383. ** String Procedures.
  384. ******************************************************************************
  385. */
  386.  
  387. /* Exit the program, and do any cleanup necessary. Note that if we had     */
  388. /* a non-fatal error, and we are in the -Q loop mode, then we won't        */
  389. /* actually terminate the program, but drop back to the command line loop. */
  390.  
  391. void Terminate(tc)
  392. int tc;
  393. {
  394.   char sz[cchSzDef];
  395.  
  396.   if (tc == tcForce) {
  397.     S = stdout;
  398.     AnsiColor(kWhite);
  399.     sprintf(sz, "\n%s %s exited.\n", szAppName, szVersionCore);
  400.     PrintSz(sz);
  401.   }
  402.   if (tc == tcError && us.fLoop)
  403.     return;
  404.   if (us.fAnsi) {
  405.     sprintf(sz, "%c[0m", chEscape);    /* Get out of any Ansi color mode. */
  406.     PrintSz(sz);
  407.   }
  408.   exit(abs(tc));
  409. }
  410.  
  411.  
  412. /* Print a string on the screen. A seemingly simple operation, however we */
  413. /* keep track of what column we are printing at after each newline so we  */
  414. /* can automatically clip at the appropriate point, and we keep track of  */
  415. /* the row we are printing at, so we may prompt before screen scrolling.  */
  416.  
  417. void PrintSz(sz)
  418. CONST char *sz;
  419. {
  420.   char szInput[cchSzDef], *pch;
  421.   int nT;
  422.  
  423.   for (pch = (char *)sz; *pch; pch++) {
  424.     if (*pch != '\n') {
  425.       is.cchCol++;
  426.       if (us.fClip80 && is.cchCol >= us.nScreenWidth)  /* Clip if need be. */
  427.         continue;
  428.     } else {
  429.       is.cchRow++;
  430.       is.cchCol = 0;
  431.     }
  432.     putc(*pch, S);
  433.     if (*pch == '\n' && us.nScrollRow > 0 && is.cchRow >= us.nScrollRow &&
  434.       S == stdout) {
  435.  
  436.       /* If we've printed 'n' rows, stop and wait for a line to be entered. */
  437.  
  438.       nT = us.fAnsi;
  439.       us.fAnsi = 0;
  440.       InputString("Press return to continue scrolling", szInput);
  441.       us.fAnsi = nT;
  442.       is.cchRow = 0;
  443.  
  444.       /* One can actually give a few simple commands before hitting return. */
  445.  
  446.       if (szInput[0] == '.' || szInput[0] == 'q')
  447.         Terminate(tcForce);
  448.       else if (szInput[0] == '8')
  449.         not(us.fClip80);
  450.       else if (szInput[0] == 'Q')
  451.         us.nScrollRow = 0;
  452.       else if (szInput[0] == 'k') {
  453.         if (us.fAnsi)
  454.           AnsiColor(kDefault);
  455.         not(us.fAnsi);
  456.       }
  457.     }
  458.   }
  459. }
  460.  
  461.  
  462. /* Print a single character on the screen. */
  463.  
  464. void PrintCh(ch)
  465. char ch;
  466. {
  467.   char sz[2];
  468.  
  469.   sz[0] = ch; sz[1] = chNull;    /* Treat char as a string of length one. */
  470.   PrintSz(sz);                   /* Then call above to print the string.  */
  471. }
  472.  
  473.  
  474. /* Print a string on the screen. Unlike the normal PrintSz(), here we still */
  475. /* go to the standard output even if text is being sent to a file with -os. */
  476.  
  477. void PrintSzScreen(sz)
  478. char *sz;
  479. {
  480.   FILE *fileT;
  481.  
  482.   fileT = S;
  483.   S = stdout;
  484.   PrintSz(sz);
  485.   S = fileT;
  486. }
  487.  
  488.  
  489. /* Print a general user message given a string. This is just like the */
  490. /* warning displayer below just that we print in a different color.   */
  491.  
  492. void PrintNotice(sz)
  493. char *sz;
  494. {
  495.   AnsiColor(kYellow);
  496.   fprintf(stderr, "%s\n", sz);
  497.   AnsiColor(kDefault); 
  498. }
  499.  
  500.  
  501. /* Print a warning message given a string. This is called in non-fatal  */
  502. /* cases where we return to normal execution after printing the string. */
  503.  
  504. void PrintWarning(sz)
  505. char *sz;
  506. {
  507.   AnsiColor(kRed);
  508.   fprintf(stderr, "%s\n", sz);
  509.   AnsiColor(kDefault); 
  510. }
  511.  
  512.  
  513. /* Print an error message. This is called in more serious cases which halt */
  514. /* running of the current chart sequence, which can terminate the program  */
  515. /* but isn't a fatal error in that we can still fall back to the -Q loop.  */
  516.  
  517. void PrintError(sz)
  518. char *sz;
  519. {
  520.   AnsiColor(kRed);
  521.   fprintf(stderr, "%s: %s\n", szAppName, sz);
  522.   Terminate(tcError);
  523.   AnsiColor(kDefault); 
  524. }
  525.  
  526.  
  527. /* Simplification for a commonly printed error message. */
  528.  
  529. void ErrorArgc(szOpt)
  530. char *szOpt;
  531. {
  532.   char sz[cchSzDef];
  533.  
  534.   sprintf(sz, "Too few options to switch %c%s", chSwitch, szOpt);
  535.   PrintError(sz);
  536. }
  537.  
  538.  
  539. /* Another simplification for a commonly printed error message. */
  540.  
  541. void ErrorValN(szOpt, nVal)
  542. char *szOpt;
  543. int nVal;
  544. {
  545.   char sz[cchSzDef];
  546.  
  547.   sprintf(sz, "Value %d passed to switch %c%s out of range.\n",
  548.     nVal, chSwitch, szOpt);
  549.   PrintError(sz);
  550. }
  551.  
  552.  
  553. /* Yet another place to print a type of error message. */
  554.  
  555. void ErrorArgv(szOpt)
  556. char *szOpt;
  557. {
  558.   char sz[cchSzDef];
  559.  
  560.   sprintf(sz, "The switch %c%s is not allowed now.\n", chSwitch, szOpt);
  561.   PrintError(sz);
  562. }
  563.  
  564.  
  565. /* Still another place to print a type of error message. */
  566.  
  567. void ErrorSwitch(szOpt)
  568. char *szOpt;
  569. {
  570.   char sz[cchSzDef];
  571.  
  572.   sprintf(sz, "Unknown switch '%s'", szOpt);
  573.   PrintError(sz);
  574. }
  575.  
  576.  
  577. /* A simple procedure used throughout Astrolog: Print a particular */
  578. /* character on the screen 'n' times.                              */
  579.  
  580. void PrintTab(ch, cch)
  581. char ch;
  582. int cch;
  583. {
  584.   int i;
  585.  
  586.   for (i = 0; i < cch; i++)
  587.     PrintCh(ch);
  588. }
  589.  
  590.  
  591. /* Set an Ansi text color. */
  592.  
  593. void AnsiColor(k)
  594. int k;
  595. {
  596.   char sz[cchSzDef];
  597.   int cchSav;
  598.  
  599.   /* Special case: If we are passed the value Reverse, and ansi is not    */
  600.   /* only on but set to a value > 1, then we'll enter reverse video mode. */
  601.  
  602.   if (!us.fAnsi || (k == kReverse && us.fAnsi < 2))
  603.     return;
  604.   cchSav = is.cchCol;
  605.   is.cchCol = 0;
  606.   sprintf(sz, "%c[", chEscape);
  607.   PrintSz(sz);
  608.   if (k == kDefault)
  609.     PrintCh('0');
  610.   else if (k == kReverse) {
  611.     PrintCh('7');
  612.   } else {
  613.     sprintf(sz, "%c;%d", k > 7 ? '1' : '0', 30 + (k & 7));
  614.     PrintSz(sz);
  615.   }
  616.   PrintCh('m');
  617.   is.cchCol = cchSav;
  618. }
  619.  
  620.  
  621. /* Print a zodiac position on the screen. This basically just prints the */
  622. /* string returned from SzZodiac() below, except we take care of color.  */
  623.  
  624. void PrintZodiac(deg)
  625. real deg;
  626. {
  627.   AnsiColor(kElemA[(int)(deg / 30.0) & 3]);
  628.   PrintSz(SzZodiac(deg));
  629.   AnsiColor(kDefault);
  630. }
  631.  
  632.  
  633. /* Given a zodiac position, return a string containing it as it's */
  634. /* formatted for display to the user.                             */
  635.  
  636. char *SzZodiac(deg)
  637. real deg;
  638. {
  639.   static char zod[11];
  640.   int sign, d, m;
  641.   real s;
  642.  
  643.   switch (us.nDegForm) {
  644.   case 0:
  645.  
  646.     /* Normally, we format the position in degrees/sign/minutes format: */
  647.  
  648.     deg = Mod(deg + (is.fSeconds ? rRound/60.0/60.0 : rRound/60.0));
  649.     sign = (int)(deg / 30.0);
  650.     d = (int)deg - sign*30;
  651.     m = (int)(RFract(deg)*60.0);
  652.     sprintf(zod, "%2d%c%c%c%02d", d, chSig3(sign + 1), m);
  653.     if (is.fSeconds) {
  654.       s = RFract(deg)*60.0; s = RFract(s)*60.0;
  655.       sprintf(&zod[7], "'%02d\"", (int)s);
  656.     }
  657.     break;
  658.  
  659.   case 1:
  660.     /* However, if -sh switch in effect, get position in hours/minutes: */
  661.  
  662.     deg = Mod(deg + (is.fSeconds ? rRound/4.0/60.0 : rRound/4.0));
  663.     d = (int)(deg / 15.0);
  664.     m = (int)((deg - (real)d*15.0)*4.0);
  665.     sprintf(zod, "%2dh,%02dm", d, m);
  666.     if (is.fSeconds) {
  667.       s = RFract(deg)*4.0; s = RFract(s)*60.0;
  668.       sprintf(&zod[7], ",%02ds", (int)s);
  669.     }
  670.     break;
  671.  
  672.   default:
  673.     /* Otherwise, if -sd in effect, format position as a simple degree: */
  674.  
  675.     sprintf(zod, is.fSeconds ? "%11.7f" : "%7.3f", deg);
  676.     break;
  677.   }
  678.   return zod;
  679. }
  680.  
  681.  
  682. /* This is similar to formatting a zodiac degree, but here we return a */
  683. /* string of a (signed) declination value in degrees and minutes.      */
  684.  
  685. char *SzAltitude(deg)
  686. real deg;
  687. {
  688.   static char alt[10];
  689.   int d, m, f;
  690.   real s;
  691.   char ch;
  692.  
  693.   f = deg < 0.0;
  694.   deg = RAbs(deg) + (is.fSeconds ? rRound/60.0/60.0 : rRound/60.0);
  695.   d = (int)deg;
  696.   m = (int)(RFract(deg)*60.0);
  697.   ch = us.fAnsi == -1 ? 128 : chDeg1;
  698.   sprintf(alt, "%c%2d%c%02d'", f ? '-' : '+', d, ch, m);
  699.   if (is.fSeconds) {
  700.     s = RFract(deg)*60.0; s = RFract(s)*60.0;
  701.     sprintf(&alt[7], "%02d\"", (int)s);
  702.   }
  703.   return alt;
  704. }
  705.  
  706.  
  707. /* Here we return a string simply expressing the given value as degrees */
  708. /* and minutes (and sometimes seconds) in the 0 to 360 degree circle.   */
  709.  
  710. char *SzDegree(deg)
  711. real deg;
  712. {
  713.   static char pos[11];
  714.   int d, m;
  715.   real s;
  716.  
  717.   deg = RAbs(deg) + (is.fSeconds ? rRound/60.0/60.0 : rRound/60.0);
  718.   d = (int)deg;
  719.   m = (int)(RFract(deg)*60.0);
  720.   sprintf(pos, "%3d%c%02d'", d, chDeg1, m);
  721.   if (is.fSeconds) {
  722.     s = RFract(deg)*60.0; s = RFract(s)*60.0;
  723.     sprintf(&pos[7], "%02d\"", (int)s);
  724.   }
  725.   return pos;
  726. }
  727.  
  728.  
  729. /* Another string formatter, here we return a date string given a month,    */
  730. /* day, and year. We format with the day or month first based on whether    */
  731. /* the "European" date variable is set or not. The routine also takes a     */
  732. /* parameter to indicate how much the string should be abbreviated, if any. */
  733.  
  734. char *SzDate(mon, day, yea, nFormat)
  735. int mon, day, yea, nFormat;
  736. {
  737.   static char szDate[20];
  738.  
  739.   if (us.fEuroDate) {
  740.     switch (nFormat) {
  741.     case  2: sprintf(szDate, "%2d %c%c%c%5d", day, chMon3(mon), yea); break;
  742.     case  1: sprintf(szDate, "%d %s %d", day, szMonth[mon], yea);     break;
  743.     case -1: sprintf(szDate, "%2d-%2d-%2d", day, mon, abs(yea)%100);  break;
  744.     default: sprintf(szDate, "%2d-%2d-%4d", day, mon, yea);           break;
  745.     }
  746.   } else {
  747.     switch (nFormat) {
  748.     case  3: sprintf(szDate, "%c%c%c %2d, %d", chMon3(mon), day, yea); break;
  749.     case  2: sprintf(szDate, "%c%c%c %2d%5d", chMon3(mon), day, yea);  break;
  750.     case  1: sprintf(szDate, "%s %d, %d", szMonth[mon], day, yea);     break;
  751.     case -1: sprintf(szDate, "%2d/%2d/%2d", mon, day, abs(yea)%100);   break;
  752.     default: sprintf(szDate, "%2d/%2d/%4d", mon, day, yea);            break;
  753.     }
  754.   }
  755.   return szDate;
  756. }
  757.  
  758.  
  759. /* Return a string containing the given time expressed as an hour and */
  760. /* minute quantity. This is formatted in 24 hour or am/pm time based  */
  761. /* on whether the "European" time format flag is set or not.          */
  762.  
  763. char *SzTime(hr, min)
  764. int hr, min;
  765. {
  766.   static char tim[8];
  767.  
  768.   if (us.fEuroTime)
  769.     sprintf(tim, "%2d:%02d", hr, min);
  770.   else
  771.     sprintf(tim, "%2d:%02d%cm", Mod12(hr), min, hr < 12 ? 'a' : 'p');
  772.   return tim;
  773. }
  774.  
  775.  
  776. /* This just determines the correct hour and minute and calls the above. */
  777.  
  778. char *SzTim(tim)
  779. real tim;
  780. {
  781.   return SzTime(NFloor(tim), (int)(RFract(RAbs(Tim))*100.0+rRound/60.0));
  782. }
  783.  
  784.  
  785. /* Return a string containing the given time zone, given as a real value     */
  786. /* having the hours before GMT in the integer part and minutes fractionally. */
  787.  
  788. char *SzZone(zon)
  789. real zon;
  790. {
  791.   static char tim[7];
  792.  
  793.   sprintf(tim, "%c%d:%02d", zon > 0.0 ? '-' : '+', (int)RAbs(zon),
  794.     (int)(RFract(RAbs(zon))*100.0+rRound/60.0));
  795.   return tim;
  796. }
  797.  
  798.  
  799. /* Nicely format the given longitude and latitude locations and return    */
  800. /* them in a string. Various parts of the program display a chart header, */
  801. /* and this allows the similar computations to be coded only once.        */
  802.  
  803. char *SzLocation(lon, lat)
  804. real lon, lat;
  805. {
  806.   static char loc[15];
  807.   int i, j;
  808.   char ch;
  809.  
  810.   i = (int)(RFract(RAbs(lon))*100.0+rRound);
  811.   j = (int)(RFract(RAbs(lat))*100.0+rRound);
  812.   ch = us.fAnsi == -1 ? 128 : chDeg1;
  813.   sprintf(loc, "%3.0f%c%02d%c%3.0f%c%02d%c",
  814.     RFloor(RAbs(lon)), ch, i, lon < 0.0 ? 'E' : 'W',
  815.     RFloor(RAbs(lat)), ch, j, lat < 0.0 ? 'S' : 'N');
  816.   return loc;
  817. }
  818.  
  819.  
  820. #ifdef TIME
  821. /* Compute the date and time it is right now as the program is running      */
  822. /* using the computer's internal clock. We do this by getting the number    */
  823. /* of seconds which have passed since January 1, 1970 and going from there. */
  824. /* The time return value filled is expressed in the given zone parameter.   */
  825.  
  826. void GetTimeNow(mon, day, yea, tim, zon)
  827. int *mon, *day, *yea;
  828. real *tim, zon;
  829. {
  830.   dword curtimer;
  831.   int min, sec;
  832.   real hr;
  833.  
  834.   time(&curtimer);
  835.   sec = (int)(curtimer % 60);
  836.   curtimer /= 60;
  837.   min = (int)(curtimer % 60);
  838.   curtimer /= 60;
  839. #ifdef MAC
  840.   curtimer += 8;
  841. #endif
  842.   hr = (real)(curtimer % 24) - zon;
  843.   curtimer /= 24;
  844.   while (hr < 0.0) {
  845.     curtimer--;
  846.     hr += 24.0;
  847.   }
  848.   while (hr >= 24.0) {
  849.     curtimer++;
  850.     hr -= 24.0;
  851.   }
  852.   curtimer += ldTime;  /* Number of days between 1/1/1970 and 1/1/4713 BC. */
  853.   JulianToMdy((real)curtimer, mon, day, yea);
  854.   *tim = hr + (real)min / 100.0 + (real)sec / 6000.0;
  855. }
  856. #endif /* TIME */
  857.  
  858.  
  859. #ifdef PCG
  860. /* Map one character value to another. This is used in processing special  */
  861. /* keys and alt key combinations, which are read in from the keyboard as a */
  862. /* zero immediately followed by some value. This converts that value into  */
  863. /* something more useful to process and deal with.                         */
  864.  
  865. int NFromAltN(nAlt)
  866. int nAlt;
  867. {
  868.   /* Map number pad keys to the numbers characters they correspond to. */
  869.   if (nAlt == 82)
  870.     return '0';
  871.   else if (FBetween(nAlt, 79, 81))
  872.     return '1' + nAlt - 79;
  873.   else if (FBetween(nAlt, 75, 77))
  874.     return '4' + nAlt - 75;
  875.   else if (FBetween(nAlt, 71, 73))
  876.     return '7' + nAlt - 71;
  877.  
  878.   /* Map F1 through F12 function keys to the values 201-212. */
  879.   else if (FBetween(nAlt, 59, 68))
  880.     return 201 + nAlt - 59;
  881.   else if (FBetween(nAlt, 133, 134))
  882.     return 211 + nAlt - 133;
  883.  
  884.   /* Map Shift+F1 through Shift+F12 keys to the values 213-224. */
  885.   else if (FBetween(nAlt, 84, 93))
  886.     return 213 + nAlt - 84;
  887.   else if (FBetween(nAlt, 135, 136))
  888.     return 223 + nAlt - 135;
  889.  
  890.   /* Map Control+F1 through Control+F12 keys to the values 225-236. */
  891.   else if (FBetween(nAlt, 94, 103))
  892.     return 225 + nAlt - 94;
  893.   else if (FBetween(nAlt, 137, 138))
  894.     return 235 + nAlt - 137;
  895.  
  896.   /* Map Alt+F1 through Alt+F12 keys to the values 237-248. */
  897.   else if (FBetween(nAlt, 104, 113))
  898.     return 237 + nAlt - 104;
  899.   else if (FBetween(nAlt, 139, 140))
  900.     return 247 + nAlt - 139;
  901.  
  902.   return chNull;
  903. }
  904. #endif
  905.  
  906.  
  907. /* Given a string representing the complete pathname to a file, strip off    */
  908. /* all the path information leaving just the filename itself. This is called */
  909. /* by the main program to determine the name of the Astrolog executable.     */
  910.  
  911. char *ProcessProgname(szPath)
  912. char *szPath;
  913. {
  914.   char *b, *c, *e;
  915.  
  916.   b = c = szPath;
  917.   while (*c) {
  918. #ifdef PC
  919.     *c = ChUncap(*c);    /* Because DOS filenames are case insensitive. */
  920. #endif
  921.     c++;
  922.   }
  923.   e = c;
  924.   while (c > b && *c != '.')
  925.     c--;
  926.   if (c > b)
  927.     *c = 0;
  928.   else
  929.     c = e;
  930.   while (c > b && *c != chDirSep)
  931.     c--;
  932.   if (c > b)
  933.     szPath = c+1;
  934.   return szPath;
  935. }
  936.  
  937.  
  938. /* Given a string, return a pointer to a persistent version of it, where  */
  939. /* 'persistent' means its contents won't be invalidated when the stack    */
  940. /* frame changes. Strings such as macros, et al, need to be in their own  */
  941. /* space and can't just be local variables in a function reading them in. */
  942.  
  943. char *SzPersist(szSrc)
  944. char *szSrc;
  945. {
  946.   char szT[cchSzDef], *szNew;
  947.   int cb;
  948.  
  949.   /* Some strings such as outer level command line parameter arguments */
  950.   /* already persist, so we can just return the same string passed in. */ 
  951.   if (is.fSzPersist)
  952.     return szSrc;
  953.  
  954.   /* Otherwise we make a copy of the string in the local heap and use it. */
  955.   cb = CchSz(szSrc)+1;
  956.   AllocateNear(szNew, cb);
  957.   if (szNew == NULL) {
  958.     sprintf(szT, "%s: Not enough near memory for string (%d bytes).",
  959.       szAppName, cb);
  960.     PrintWarning(szT);
  961.   } else
  962.     CopyRgb((byte *)szSrc, (byte *)szNew, cb);
  963.   return szNew;
  964. }
  965.  
  966.  
  967. /* This is Astrolog's memory allocation routine, returning a pointer given */
  968. /* a size, a flag for if it is a more than 64K huge allocation, and a      */
  969. /* string to use when printing an error if the allocation fails.           */
  970.  
  971. lpbyte PAllocate(lcb, fHuge, szType)
  972. long lcb;
  973. bool fHuge;
  974. char *szType;
  975. {
  976.   char szT[cchSzDef];
  977.   lpbyte lp;
  978.  
  979.   if (fHuge)
  980.     AllocateHuge(lp, lcb);
  981.   else
  982.     AllocateFar(lp, (int)lcb);
  983. #ifdef PC
  984.   /* For PC's the array better not cross a segment boundary. */
  985.   if (lp && !fHuge && WHi(WLo(lp) + lcb) > 0)
  986.     lp = NULL;
  987. #endif
  988.   if (lp == NULL && szType) {
  989.     sprintf(szT, "%s: Not enough memory for %s (%ld bytes).",
  990.       szAppName, szType, lcb);
  991.     PrintWarning(szT);
  992.   }
  993.   return lp;
  994. }
  995.  
  996. /* general.c */
  997.